package cn.skycity.auth.config;

import cn.skycity.auth.authentication.handler.CustomerErrorResponseHandler;
import cn.skycity.common.model.Result;
import cn.skycity.common.model.ResultCode;
import cn.skycity.common.utils.ResponseUtils;
import jakarta.annotation.Resource;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.web.client.RestOperations;

import java.time.Duration;

@Configuration
public class SecurityConfig {
    private static final String CUSTOM_CONSENT_PAGE_URI = "/oauth2/consent";
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private ApplicationProperties properties;
    @Resource
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    @Resource
    private CustomerErrorResponseHandler customerErrorResponseHandler;

//    @Bean
//    @Order(1)
//    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
//            throws Exception {
//        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
//        authorizationServerConfigurer.tokenEndpoint(tokenEndpoint->{
//            tokenEndpoint.accessTokenRequestConverter(new DelegatingAuthenticationConverter(
//                Arrays.asList(
//                    new OAuth2AuthorizationCodeAuthenticationConverter(),
//                    new OAuth2RefreshTokenAuthenticationConverter(),
//                    new OAuth2ClientCredentialsAuthenticationConverter(),
//                    new OAuth2ResourcePasswordAuthenticationConverter())))
//                    .accessTokenResponseHandler(authenticationSuccessHandler)
//                    .errorResponseHandler(customerErrorResponseHandler);
//        });
//
//        //定义授权同意页面
//        authorizationServerConfigurer.authorizationEndpoint(authorizationEndpoint ->
//                authorizationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI));
//        RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
//        authorizationServerConfigurer.oidc(Customizer.withDefaults());    // Enable OpenID Connect 1.0
//
//        http.authorizeHttpRequests(authorizeRequests ->
//                    authorizeRequests.anyRequest().authenticated()
//                )
//                .csrf(AbstractHttpConfigurer::disable)
//                .apply(authorizationServerConfigurer).
//                and().
//                // Redirect to the login page when not authenticated from the
//                // authorization endpoint
//                        exceptionHandling(exceptions -> exceptions.
//                        authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
//                .userDetailsService(userDetailsService).formLogin(Customizer.withDefaults());
//
//        SecurityFilterChain securityFilterChain = http.build();
//
//
//        OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
//        OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator =  http.getSharedObject(OAuth2TokenGenerator.class);
//        OAuth2ResourcePasswordAuthenticationProvider resourcePasswordAuthenticationProvider =
//                new OAuth2ResourcePasswordAuthenticationProvider(authorizationService,userDetailsService,tokenGenerator,passwordEncoder());
//        http.authenticationProvider(resourcePasswordAuthenticationProvider);
//
//        return securityFilterChain;
//    }

    @Bean
    @Order(2)
    public SecurityFilterChain filterChain(HttpSecurity http)
            throws Exception {
        http.authorizeHttpRequests((authorize) ->
//                authorize.antMatchers(properties.getSecurity().getExcludeUrls())
                authorize.requestMatchers(new String[]{"/login"}).permitAll()
                                .anyRequest().authenticated()
        ).csrf(AbstractHttpConfigurer::disable)
                .logout((logout) ->
                        logout.logoutUrl( "/logout").logoutSuccessHandler((request, response, authentication) -> {
                    ResponseUtils.responseSucceed(response, Result.success(ResultCode.SUCCESS));
                }));//.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
        //在AuthorizationFilter执行执行先执行该Fileter，从httpRequest头中查询到accessToken<，最后放入到SecurityContextHolder.getContext().setAuthentication()放入到当前线程中
        return http.build();
    }


    /**
     * 设置加密方式
     * 将密码加密方式采用委托方式，默认以BCryptPasswordEncoder方式进行加密，但兼容ldap,MD4,MD5等方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public OpaqueTokenIntrospector introspector(RestTemplateBuilder builder, OAuth2ResourceServerProperties properties) {
        RestOperations rest = builder
                .basicAuthentication(properties.getOpaquetoken().getClientId(), properties.getOpaquetoken().getClientSecret())
                .setConnectTimeout(Duration.ofSeconds(60))
                .setReadTimeout(Duration.ofSeconds(60))
                .build();
        return new SpringOpaqueTokenIntrospector(properties.getOpaquetoken().getIntrospectionUri(), rest);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }

}
